package com.zstk.mongo.service;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 数据库操作服务类
 * 仿mybatis操作数据库方法
 * </p>
 *
 * @author zxt
 * @since 2022/7/29 9:23
 */
@Component
public class MongoService {
    private static final Query EMPTY_QUERY = new BasicQuery("{}");
    @Resource
    MongoTemplate mongoTemplate;

    /**
     * 插入新对象
     */
    public <T> void save(T entity) {
        mongoTemplate.save(entity);
    }

    /**
     * 批量保存对象
     */
    public <T> void saveBatch(Collection<T> entityList) {
        mongoTemplate.insertAll(entityList);
    }

    /**
     * 根据id删除数据
     *
     * @param id    数据唯一标识
     * @param clazz 对象类型
     */
    public void removeById(Serializable id, Class<?> clazz) {
        mongoTemplate.remove(idEqQuery(id), clazz);
    }

    /**
     * 根据map 条件删除
     *
     * @param columnMap 条件集合
     * @param clazz     对象类型
     */
    public void removeByMap(Map<String, Object> columnMap, Class<?> clazz) {
        mongoTemplate.remove(eqQuery(columnMap), clazz);
    }

    /**
     * 根据唯一标识列表删除数据
     *
     * @param idList 唯一标识列表
     * @param clazz  对象类型
     */
    public void removeByIds(Collection<? extends Serializable> idList, Class<?> clazz) {
        mongoTemplate.remove(idInQuery(idList), clazz);
    }

    /**
     * 根据条件删除
     *
     * @param query 条件对象
     * @param clazz 对象类型
     */
    public void remove(Query query, Class<?> clazz) {
        mongoTemplate.remove(query, clazz);
    }

    /**
     * 根据唯一标识更新
     *
     * @param entity 对象实例
     */
    public <T> void updateById(T entity) {
        if (ObjectUtil.isNotNull(entity)) {
            JSONObject obj = (JSONObject) JSONObject.toJSON(entity);
            DBObject update = new BasicDBObject();
            update.put("$set", obj);
            mongoTemplate.updateFirst(idEqQuery(getIdValue(entity)),
                                      new BasicUpdate(update.toString()),
                                      entity.getClass());
        }
    }

    /**
     * 批量更新
     *
     * @param entityList 对象集合
     */
    public <T> void updateBatchById(Collection<T> entityList) {
        entityList.forEach(this::updateById);
    }

    /**
     * 更新
     *
     * @param query  条件
     * @param update 更新对象
     * @param clazz  对象类型
     */
    public void update(Query query, Update update, Class<?> clazz) {
        mongoTemplate.updateMulti(query, update, clazz);
    }

    /**
     * 新增或更新
     */
    public <T> void saveOrUpdate(T entity) {
        if (ObjectUtil.isNotNull(entity)) {
            String key = JSONObject.toJSONString(entity);
            Update inc = new Update().inc(key, 1);
            mongoTemplate.upsert(idEqQuery(getIdValue(entity)), inc, entity.getClass());
        }
    }

    /**
     * 批量新增或更新
     */
    public <T> void saveOrUpdateBatch(Collection<T> entityList) {
        entityList.forEach(this::saveOrUpdate);
    }

    /**
     * 根据唯一标识查询
     */
    public <T> T getById(Serializable id, Class<T> clazz) {
        return mongoTemplate.findById(id, clazz);
    }

    /**
     * 根据查询条件查询对象
     *
     * @param query 查询条件对象
     * @param clazz 对象类型
     */
    public <T> T getOne(Query query, Class<T> clazz) {
        return mongoTemplate.findOne(query, clazz);
    }

    /**
     * 根据唯一标识列表查询
     */
    public <T> List<T> listByIds(Collection<? extends Serializable> idList, Class<T> clazz) {
        return mongoTemplate.find(idInQuery(idList), clazz);
    }

    /**
     * 根据Map集合条件查询
     */
    public <T> List<T> listByMap(Map<String, Object> columnMap, Class<T> clazz) {
        return mongoTemplate.find(eqQuery(columnMap), clazz);
    }

    /**
     * 列表查询
     *
     * @param clazz 对象列表
     */
    public <T> List<T> list(Class<T> clazz) {
        return mongoTemplate.findAll(clazz);
    }

    /**
     * 列表查询
     *
     * @param query 条件对象
     */
    public <T> List<T> list(Query query, Class<T> clazz) {
        return mongoTemplate.find(query, clazz);
    }

    /**
     * 统计
     */
    public <T> long count(Class<T> clazz) {
        return mongoTemplate.count(EMPTY_QUERY, clazz);
    }

    /**
     * 统计
     *
     * @param query 统计条件对象
     */
    public <T> long count(Query query, Class<T> clazz) {
        return mongoTemplate.count(query, clazz);
    }

    /**
     * 分页查询
     *
     * @param page  分页对象
     * @param clazz 查询的对象类型
     *
     * @return 默认按唯一标识ID倒叙返回
     */
    public <T> IPage<T> page(IPage<T> page, Class<T> clazz) {
        page.setTotal(count(clazz));
        Pageable pageable = PageRequest.of((int) (page.getCurrent() + 1), (int) page.getSize());
        List<T> records = mongoTemplate.find(new Query().with(Sort.by(Sort.Order.desc("id"))).with(pageable), clazz);
        page.setPages(page.getPages());
        page.setRecords(records);
        return page;
    }

    /**
     * 分页查询
     *
     * @param page  分页对象
     * @param query 分页查询条件对象
     * @param clazz 查询的对象类型
     */
    public <T> IPage<T> page(IPage<T> page, Query query, Class<T> clazz) {
        page.setTotal(count(query, clazz));
        Pageable pageable = PageRequest.of((int) (page.getCurrent() + 1), (int) page.getSize());
        List<T> records = mongoTemplate.find(query.with(pageable), clazz);
        page.setPages(page.getPages());
        page.setRecords(records);
        return page;
    }

    /**
     * 唯一标识ID 构建查询对象
     */
    private Query idEqQuery(Serializable id) {
        Criteria criteria = Criteria.where("id").is(id);
        return Query.query(criteria);
    }

    /**
     * 唯一标识列表构建查询条件In
     */
    private Query idInQuery(Collection<? extends Serializable> idList) {
        Criteria criteria = Criteria.where("id").in(idList);
        return Query.query(criteria);
    }

    /**
     * map条件构建查询对象
     */
    private Query eqQuery(Map<String, Object> data) {
        if (CollectionUtils.isEmpty(data)) {
            return EMPTY_QUERY;
        } else {
            Criteria criteria = new Criteria();
            data.forEach((k, v) -> criteria.and(k).is(v));
            return Query.query(criteria);
        }
    }

    /**
     * 获取对象唯一标识ID
     */
    private <T> Serializable getIdValue(T entity) {
        try {
            Field field = entity.getClass().getDeclaredField("id");
            field.setAccessible(true);
            return (Serializable) field.get(entity);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

}
